#region Copyright (c) 2006-2011 Widgetsphere LLC, All Rights Reserved
//--------------------------------------------------------------------- *
//                          Widgetsphere  LLC                           *
//             Copyright (c) 2006-2011 All Rights reserved              *
//                                                                      *
//                                                                      *
//This file and its contents are protected by United States and         *
//International copyright laws.  Unauthorized reproduction and/or       *
//distribution of all or any portion of the code contained herein       *
//is strictly prohibited and will result in severe civil and criminal   *
//penalties.  Any violations of this copyright will be prosecuted       *
//to the fullest extent possible under law.                             *
//                                                                      *
//THE SOURCE CODE CONTAINED HEREIN AND IN RELATED FILES IS PROVIDED     *
//TO THE REGISTERED DEVELOPER FOR THE PURPOSES OF EDUCATION AND         *
//TROUBLESHOOTING. UNDER NO CIRCUMSTANCES MAY ANY PORTION OF THE SOURCE *
//CODE BE DISTRIBUTED, DISCLOSED OR OTHERWISE MADE AVAILABLE TO ANY     *
//THIRD PARTY WITHOUT THE EXPRESS WRITTEN CONSENT OF WIDGETSPHERE LLC   *
//                                                                      *
//UNDER NO CIRCUMSTANCES MAY THE SOURCE CODE BE USED IN WHOLE OR IN     *
//PART, AS THE BASIS FOR CREATING A PRODUCT THAT PROVIDES THE SAME, OR  *
//SUBSTANTIALLY THE SAME, FUNCTIONALITY AS ANY WIDGETSPHERE PRODUCT.    *
//                                                                      *
//THE REGISTERED DEVELOPER ACKNOWLEDGES THAT THIS SOURCE CODE           *
//CONTAINS VALUABLE AND PROPRIETARY TRADE SECRETS OF WIDGETSPHERE,      *
//INC.  THE REGISTERED DEVELOPER AGREES TO EXPEND EVERY EFFORT TO       *
//INSURE ITS CONFIDENTIALITY.                                           *
//                                                                      *
//THE END USER LICENSE AGREEMENT (EULA) ACCOMPANYING THE PRODUCT        *
//PERMITS THE REGISTERED DEVELOPER TO REDISTRIBUTE THE PRODUCT IN       *
//EXECUTABLE FORM ONLY IN SUPPORT OF APPLICATIONS WRITTEN USING         *
//THE PRODUCT.  IT DOES NOT PROVIDE ANY RIGHTS REGARDING THE            *
//SOURCE CODE CONTAINED HEREIN.                                         *
//                                                                      *
//THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE.              *
//--------------------------------------------------------------------- *
#endregion
using System;
using System.Linq;
using Widgetsphere.Generator.Common.GeneratorFramework;
using Widgetsphere.Generator.Models;
using System.Text;
using Widgetsphere.Generator.Common.Util;
using System.Collections.Generic;
using Widgetsphere.Generator.Common;

namespace Widgetsphere.Generator.EFSelfTrackingClient.Generators.Contexts
{
	public class ContextGeneratedTemplate : EFSelfTrackingClientBaseTemplate
	{
		private StringBuilder sb = new StringBuilder();

		public ContextGeneratedTemplate(ModelRoot model)
		{
			_model = model;
		}

		#region BaseClassTemplate overrides
		public override string FileName
		{
			get { return "ModelCore.Generated.cs"; }
		}

		public string ParentItemName
		{
			get { return "ModelCore.cs"; }
		}

		public override string FileContent
		{
			get
			{
				GenerateContent();
				return sb.ToString();
			}
		}
		#endregion

		#region GenerateContent

		private void GenerateContent()
		{
			try
			{
				Widgetsphere.Generator.GenerationHelper.AppendCopyrightInCode(sb, _model);
				this.AppendUsingStatements();
				sb.AppendLine("namespace " + this.GetLocalNamespace());
				sb.AppendLine("{");
				this.AppendClass();
				sb.AppendLine("}");
				sb.AppendLine();
			}
			catch (Exception ex)
			{
				throw;
			}
		}

		private void AppendUsingStatements()
		{
			sb.AppendLine("using System;");
			sb.AppendLine("using System.Collections.Generic;");
			sb.AppendLine("using System.Collections.ObjectModel;");
			sb.AppendLine("using System.Collections.Specialized;");
			sb.AppendLine("using System.ComponentModel;");
			sb.AppendLine("using System.Globalization;");
			sb.AppendLine("using System.Runtime.Serialization;");
			sb.AppendLine();
		}

		private void AppendClass()
		{
			sb.AppendLine("	// Helper class that captures most of the change tracking work that needs to be done");
			sb.AppendLine("	// for self tracking entities.");
			sb.AppendLine("	[DataContract(IsReference = true)]");
			sb.AppendLine("	public class ObjectChangeTracker");
			sb.AppendLine("	{");
			sb.AppendLine("		#region Fields");
			sb.AppendLine();
			sb.AppendLine("		private bool _isDeserializing;");
			sb.AppendLine("		private ObjectState _objectState = ObjectState.Added;");
			sb.AppendLine("		private bool _changeTrackingEnabled;");
			sb.AppendLine("		private OriginalValuesDictionary _originalValues;");
			sb.AppendLine("		private ExtendedPropertiesDictionary _extendedProperties;");
			sb.AppendLine("		private ObjectsAddedToCollectionProperties _objectsAddedToCollections = new ObjectsAddedToCollectionProperties();");
			sb.AppendLine("		private ObjectsRemovedFromCollectionProperties _objectsRemovedFromCollections = new ObjectsRemovedFromCollectionProperties();");
			sb.AppendLine();
			sb.AppendLine("		#endregion");
			sb.AppendLine();
			sb.AppendLine("		#region Events");
			sb.AppendLine();
			sb.AppendLine("		public event EventHandler<ObjectStateChangingEventArgs> ObjectStateChanging;");
			sb.AppendLine();
			sb.AppendLine("		#endregion");
			sb.AppendLine();
			sb.AppendLine("		protected virtual void OnObjectStateChanging(ObjectState newState)");
			sb.AppendLine("		{");
			sb.AppendLine("			if (ObjectStateChanging != null)");
			sb.AppendLine("			{");
			sb.AppendLine("				ObjectStateChanging(this, new ObjectStateChangingEventArgs() { NewState = newState });");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		[DataMember]");
			sb.AppendLine("		public virtual ObjectState State");
			sb.AppendLine("		{");
			sb.AppendLine("			get { return _objectState; }");
			sb.AppendLine("			set");
			sb.AppendLine("			{");
			sb.AppendLine("				if (_isDeserializing || _changeTrackingEnabled)");
			sb.AppendLine("				{");
			sb.AppendLine("					OnObjectStateChanging(value);");
			sb.AppendLine("					_objectState = value;");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		public bool ChangeTrackingEnabled");
			sb.AppendLine("		{");
			sb.AppendLine("			get { return _changeTrackingEnabled; }");
			sb.AppendLine("			set { _changeTrackingEnabled = value; }");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		// Returns the removed objects to collection valued properties that were changed.");
			sb.AppendLine("		[DataMember]");
			sb.AppendLine("		public virtual ObjectsRemovedFromCollectionProperties ObjectsRemovedFromCollectionProperties");
			sb.AppendLine("		{");
			sb.AppendLine("			get");
			sb.AppendLine("			{");
			sb.AppendLine("				if (_objectsRemovedFromCollections == null)");
			sb.AppendLine("				{");
			sb.AppendLine("					_objectsRemovedFromCollections = new ObjectsRemovedFromCollectionProperties();");
			sb.AppendLine("				}");
			sb.AppendLine("				return _objectsRemovedFromCollections;");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		// Returns the original values for properties that were changed.");
			sb.AppendLine("		[DataMember]");
			sb.AppendLine("		public virtual OriginalValuesDictionary OriginalValues");
			sb.AppendLine("		{");
			sb.AppendLine("			get");
			sb.AppendLine("			{");
			sb.AppendLine("				if (_originalValues == null)");
			sb.AppendLine("				{");
			sb.AppendLine("					_originalValues = new OriginalValuesDictionary();");
			sb.AppendLine("				}");
			sb.AppendLine("				return _originalValues;");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		// Returns the extended property values.");
			sb.AppendLine("		// This includes key values for independent associations that are needed for the");
			sb.AppendLine("		// concurrency model in the Entity Framework");
			sb.AppendLine("		[DataMember]");
			sb.AppendLine("		public virtual ExtendedPropertiesDictionary ExtendedProperties");
			sb.AppendLine("		{");
			sb.AppendLine("			get");
			sb.AppendLine("			{");
			sb.AppendLine("				if (_extendedProperties == null)");
			sb.AppendLine("				{");
			sb.AppendLine("					_extendedProperties = new ExtendedPropertiesDictionary();");
			sb.AppendLine("				}");
			sb.AppendLine("				return _extendedProperties;");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		// Returns the added objects to collection valued properties that were changed.");
			sb.AppendLine("		[DataMember]");
			sb.AppendLine("		public virtual ObjectsAddedToCollectionProperties ObjectsAddedToCollectionProperties");
			sb.AppendLine("		{");
			sb.AppendLine("			get");
			sb.AppendLine("			{");
			sb.AppendLine("				if (_objectsAddedToCollections == null)");
			sb.AppendLine("				{");
			sb.AppendLine("					_objectsAddedToCollections = new ObjectsAddedToCollectionProperties();");
			sb.AppendLine("				}");
			sb.AppendLine("				return _objectsAddedToCollections;");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		#region MethodsForChangeTrackingOnClient");
			sb.AppendLine();
			sb.AppendLine("		[OnDeserializing]");
			sb.AppendLine("		public void OnDeserializingMethod(StreamingContext context)");
			sb.AppendLine("		{");
			sb.AppendLine("			_isDeserializing = true;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		[OnDeserialized]");
			sb.AppendLine("		public void OnDeserializedMethod(StreamingContext context)");
			sb.AppendLine("		{");
			sb.AppendLine("			_isDeserializing = false;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		// Resets the ObjectChangeTracker to the Unchanged state and");
			sb.AppendLine("		// clears the original values as well as the record of changes");
			sb.AppendLine("		// to collection properties");
			sb.AppendLine("		public void AcceptChanges()");
			sb.AppendLine("		{");
			sb.AppendLine("			OnObjectStateChanging(ObjectState.Unchanged);");
			sb.AppendLine("			OriginalValues.Clear();");
			sb.AppendLine("			ObjectsAddedToCollectionProperties.Clear();");
			sb.AppendLine("			ObjectsRemovedFromCollectionProperties.Clear();");
			sb.AppendLine("			ChangeTrackingEnabled = true;");
			sb.AppendLine("			_objectState = ObjectState.Unchanged;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		// Captures the original value for a property that is changing.");
			sb.AppendLine("		internal void RecordOriginalValue(string propertyName, object value)");
			sb.AppendLine("		{");
			sb.AppendLine("			if (_changeTrackingEnabled && _objectState != ObjectState.Added)");
			sb.AppendLine("			{");
			sb.AppendLine("				if (!OriginalValues.ContainsKey(propertyName))");
			sb.AppendLine("				{");
			sb.AppendLine("					OriginalValues[propertyName] = value;");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		// Records an addition to collection valued properties on SelfTracking Entities.");
			sb.AppendLine("		internal void RecordAdditionToCollectionProperties(string propertyName, object value)");
			sb.AppendLine("		{");
			sb.AppendLine("			if (_changeTrackingEnabled)");
			sb.AppendLine("			{");
			sb.AppendLine("				// Add the entity back after deleting it, we should do nothing here then");
			sb.AppendLine("				if (ObjectsRemovedFromCollectionProperties.ContainsKey(propertyName)");
			sb.AppendLine("						&& ObjectsRemovedFromCollectionProperties[propertyName].Contains(value))");
			sb.AppendLine("				{");
			sb.AppendLine("					ObjectsRemovedFromCollectionProperties[propertyName].Remove(value);");
			sb.AppendLine("					if (ObjectsRemovedFromCollectionProperties[propertyName].Count == 0)");
			sb.AppendLine("					{");
			sb.AppendLine("						ObjectsRemovedFromCollectionProperties.Remove(propertyName);");
			sb.AppendLine("					}");
			sb.AppendLine("					return;");
			sb.AppendLine("				}");
			sb.AppendLine();
			sb.AppendLine("				if (!ObjectsAddedToCollectionProperties.ContainsKey(propertyName))");
			sb.AppendLine("				{");
			sb.AppendLine("					ObjectsAddedToCollectionProperties[propertyName] = new ObjectList();");
			sb.AppendLine("					ObjectsAddedToCollectionProperties[propertyName].Add(value);");
			sb.AppendLine("				}");
			sb.AppendLine("				else");
			sb.AppendLine("				{");
			sb.AppendLine("					ObjectsAddedToCollectionProperties[propertyName].Add(value);");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		// Records a removal to collection valued properties on SelfTracking Entities.");
			sb.AppendLine("		internal void RecordRemovalFromCollectionProperties(string propertyName, object value)");
			sb.AppendLine("		{");
			sb.AppendLine("			if (_changeTrackingEnabled)");
			sb.AppendLine("			{");
			sb.AppendLine("				// Delete the entity back after adding it, we should do nothing here then");
			sb.AppendLine("				if (ObjectsAddedToCollectionProperties.ContainsKey(propertyName)");
			sb.AppendLine("						&& ObjectsAddedToCollectionProperties[propertyName].Contains(value))");
			sb.AppendLine("				{");
			sb.AppendLine("					ObjectsAddedToCollectionProperties[propertyName].Remove(value);");
			sb.AppendLine("					if (ObjectsAddedToCollectionProperties[propertyName].Count == 0)");
			sb.AppendLine("					{");
			sb.AppendLine("						ObjectsAddedToCollectionProperties.Remove(propertyName);");
			sb.AppendLine("					}");
			sb.AppendLine("					return;");
			sb.AppendLine("				}");
			sb.AppendLine();
			sb.AppendLine("				if (!ObjectsRemovedFromCollectionProperties.ContainsKey(propertyName))");
			sb.AppendLine("				{");
			sb.AppendLine("					ObjectsRemovedFromCollectionProperties[propertyName] = new ObjectList();");
			sb.AppendLine("					ObjectsRemovedFromCollectionProperties[propertyName].Add(value);");
			sb.AppendLine("				}");
			sb.AppendLine("				else");
			sb.AppendLine("				{");
			sb.AppendLine("					if (!ObjectsRemovedFromCollectionProperties[propertyName].Contains(value))");
			sb.AppendLine("					{");
			sb.AppendLine("						ObjectsRemovedFromCollectionProperties[propertyName].Add(value);");
			sb.AppendLine("					}");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine("		#endregion");
			sb.AppendLine("	}");
			sb.AppendLine();
			sb.AppendLine("	#region EnumForObjectState");
			sb.AppendLine("	[Flags]");
			sb.AppendLine("	public enum ObjectState");
			sb.AppendLine("	{");
			sb.AppendLine("		Unchanged = 0x1,");
			sb.AppendLine("		Added = 0x2,");
			sb.AppendLine("		Modified = 0x4,");
			sb.AppendLine("		Deleted = 0x8");
			sb.AppendLine("	}");
			sb.AppendLine("	#endregion");
			sb.AppendLine();
			sb.AppendLine("	[CollectionDataContract(Name = \"ObjectsAddedToCollectionProperties\",");
			sb.AppendLine("			ItemName = \"AddedObjectsForProperty\", KeyName = \"CollectionPropertyName\", ValueName = \"AddedObjects\")]");
			sb.AppendLine("	public class ObjectsAddedToCollectionProperties : Dictionary<string, ObjectList> { }");
			sb.AppendLine();
			sb.AppendLine("	[CollectionDataContract(Name = \"ObjectsRemovedFromCollectionProperties\",");
			sb.AppendLine("			ItemName = \"DeletedObjectsForProperty\", KeyName = \"CollectionPropertyName\", ValueName = \"DeletedObjects\")]");
			sb.AppendLine("	public class ObjectsRemovedFromCollectionProperties : Dictionary<string, ObjectList> { }");
			sb.AppendLine();
			sb.AppendLine("	[CollectionDataContract(Name = \"OriginalValuesDictionary\",");
			sb.AppendLine("			ItemName = \"OriginalValues\", KeyName = \"Name\", ValueName = \"OriginalValue\")]");
			sb.AppendLine("	public class OriginalValuesDictionary : Dictionary<string, Object> { }");
			sb.AppendLine();
			sb.AppendLine("	[CollectionDataContract(Name = \"ExtendedPropertiesDictionary\",");
			sb.AppendLine("			ItemName = \"ExtendedProperties\", KeyName = \"Name\", ValueName = \"ExtendedProperty\")]");
			sb.AppendLine("	public class ExtendedPropertiesDictionary : Dictionary<string, Object> { }");
			sb.AppendLine();
			sb.AppendLine("	[CollectionDataContract(ItemName = \"ObjectValue\")]");
			sb.AppendLine("	public class ObjectList : List<object> { }");
			sb.AppendLine("	// The interface is implemented by the self tracking entities that EF will generate.");
			sb.AppendLine("	// We will have an Adapter that converts this interface to the interface that the EF expects.");
			sb.AppendLine("	// The Adapter will live on the server side.");
			sb.AppendLine("	public interface IObjectWithChangeTracker");
			sb.AppendLine("	{");
			sb.AppendLine("		// Has all the change tracking information for the subgraph of a given object.");
			sb.AppendLine("		ObjectChangeTracker ChangeTracker { get; }");
			sb.AppendLine("	}");
			sb.AppendLine();
			sb.AppendLine("	public class ObjectStateChangingEventArgs : EventArgs");
			sb.AppendLine("	{");
			sb.AppendLine("		public ObjectState NewState { get; set; }");
			sb.AppendLine("	}");
			sb.AppendLine();
			sb.AppendLine("	public static class ObjectWithChangeTrackerExtensions");
			sb.AppendLine("	{");
			sb.AppendLine("		public static T MarkAsDeleted<T>(this T trackingItem) where T : IObjectWithChangeTracker");
			sb.AppendLine("		{");
			sb.AppendLine("			if (trackingItem == null)");
			sb.AppendLine("			{");
			sb.AppendLine("				throw new ArgumentNullException(\"trackingItem\");");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			trackingItem.ChangeTracker.ChangeTrackingEnabled = true;");
			sb.AppendLine("			trackingItem.ChangeTracker.State = ObjectState.Deleted;");
			sb.AppendLine("			return trackingItem;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		public static T MarkAsAdded<T>(this T trackingItem) where T : IObjectWithChangeTracker");
			sb.AppendLine("		{");
			sb.AppendLine("			if (trackingItem == null)");
			sb.AppendLine("			{");
			sb.AppendLine("				throw new ArgumentNullException(\"trackingItem\");");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			trackingItem.ChangeTracker.ChangeTrackingEnabled = true;");
			sb.AppendLine("			trackingItem.ChangeTracker.State = ObjectState.Added;");
			sb.AppendLine("			return trackingItem;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		public static T MarkAsModified<T>(this T trackingItem) where T : IObjectWithChangeTracker");
			sb.AppendLine("		{");
			sb.AppendLine("			if (trackingItem == null)");
			sb.AppendLine("			{");
			sb.AppendLine("				throw new ArgumentNullException(\"trackingItem\");");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			trackingItem.ChangeTracker.ChangeTrackingEnabled = true;");
			sb.AppendLine("			trackingItem.ChangeTracker.State = ObjectState.Modified;");
			sb.AppendLine("			return trackingItem;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		public static T MarkAsUnchanged<T>(this T trackingItem) where T : IObjectWithChangeTracker");
			sb.AppendLine("		{");
			sb.AppendLine("			if (trackingItem == null)");
			sb.AppendLine("			{");
			sb.AppendLine("				throw new ArgumentNullException(\"trackingItem\");");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			trackingItem.ChangeTracker.ChangeTrackingEnabled = true;");
			sb.AppendLine("			trackingItem.ChangeTracker.State = ObjectState.Unchanged;");
			sb.AppendLine("			return trackingItem;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		public static void StartTracking(this IObjectWithChangeTracker trackingItem)");
			sb.AppendLine("		{");
			sb.AppendLine("			if (trackingItem == null)");
			sb.AppendLine("			{");
			sb.AppendLine("				throw new ArgumentNullException(\"trackingItem\");");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			trackingItem.ChangeTracker.ChangeTrackingEnabled = true;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		public static void StopTracking(this IObjectWithChangeTracker trackingItem)");
			sb.AppendLine("		{");
			sb.AppendLine("			if (trackingItem == null)");
			sb.AppendLine("			{");
			sb.AppendLine("				throw new ArgumentNullException(\"trackingItem\");");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			trackingItem.ChangeTracker.ChangeTrackingEnabled = false;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		public static void AcceptChanges(this IObjectWithChangeTracker trackingItem)");
			sb.AppendLine("		{");
			sb.AppendLine("			if (trackingItem == null)");
			sb.AppendLine("			{");
			sb.AppendLine("				throw new ArgumentNullException(\"trackingItem\");");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			trackingItem.ChangeTracker.AcceptChanges();");
			sb.AppendLine("		}");
			sb.AppendLine("	}");
			sb.AppendLine();
			sb.AppendLine("	// An System.Collections.ObjectModel.ObservableCollection that raises");
			sb.AppendLine("	// individual item removal notifications on clear and prevents adding duplicates.");
			sb.AppendLine("	public class TrackableCollection<T> : ObservableCollection<T>");
			sb.AppendLine("	{");
			sb.AppendLine("		protected override void ClearItems()");
			sb.AppendLine("		{");
			sb.AppendLine("			new List<T>(this).ForEach(t => Remove(t));");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		protected override void InsertItem(int index, T item)");
			sb.AppendLine("		{");
			sb.AppendLine("			if (!this.Contains(item))");
			sb.AppendLine("			{");
			sb.AppendLine("				base.InsertItem(index, item);");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine("	}");
			sb.AppendLine();
			sb.AppendLine("	// An interface that provides an event that fires when complex properties change.");
			sb.AppendLine("	// Changes can be the replacement of a complex property with a new complex type instance or");
			sb.AppendLine("	// a change to a scalar property within a complex type instance.");
			sb.AppendLine("	public interface INotifyComplexPropertyChanging");
			sb.AppendLine("	{");
			sb.AppendLine("		event EventHandler ComplexPropertyChanging;");
			sb.AppendLine("	}");
			sb.AppendLine();
			sb.AppendLine("	public static class EqualityComparer");
			sb.AppendLine("	{");
			sb.AppendLine("		// Helper method to determine if two byte arrays are the same value even if they are different object references");
			sb.AppendLine("		public static bool BinaryEquals(object binaryValue1, object binaryValue2)");
			sb.AppendLine("		{");
			sb.AppendLine("			if (Object.ReferenceEquals(binaryValue1, binaryValue2))");
			sb.AppendLine("			{");
			sb.AppendLine("				return true;");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			byte[] array1 = binaryValue1 as byte[];");
			sb.AppendLine("			byte[] array2 = binaryValue2 as byte[];");
			sb.AppendLine();
			sb.AppendLine("			if (array1 != null && array2 != null)");
			sb.AppendLine("			{");
			sb.AppendLine("				if (array1.Length != array2.Length)");
			sb.AppendLine("				{");
			sb.AppendLine("					return false;");
			sb.AppendLine("				}");
			sb.AppendLine();
			sb.AppendLine("				for (int i = 0; i < array1.Length; i++)");
			sb.AppendLine("				{");
			sb.AppendLine("					if (array1[i] != array2[i])");
			sb.AppendLine("					{");
			sb.AppendLine("						return false;");
			sb.AppendLine("					}");
			sb.AppendLine("				}");
			sb.AppendLine();
			sb.AppendLine("				return true;");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			return false;");
			sb.AppendLine("		}");
			sb.AppendLine("	}");
			sb.AppendLine();
		}

		#endregion

	}
}